home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / scatter_hsv.c < prev    next >
Encoding:
C/C++ Source or Header  |  2003-02-13  |  17.5 KB  |  671 lines

  1. /* scatter_hsv.c -- This is a plug-in for the GIMP (1.0's API)
  2.  * Author: Shuji Narazaki <narazaki@InetQ.or.jp>
  3.  * Time-stamp: <2000-01-08 02:49:39 yasuhiro>
  4.  * Version: 0.42
  5.  *
  6.  * Copyright (C) 1997 Shuji Narazaki <narazaki@InetQ.or.jp>
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 2 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program; if not, write to the Free Software
  20.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21.  */
  22.  
  23. #include "config.h"
  24.  
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <time.h>
  28.  
  29. #include <gtk/gtk.h>
  30.  
  31. #include <libgimp/gimp.h>
  32. #include <libgimp/gimpui.h>
  33.  
  34. #include "libgimp/stdplugins-intl.h"
  35.  
  36.  
  37. #define    PLUG_IN_NAME    "plug_in_scatter_hsv"
  38. #define SHORT_NAME    "scatter_hsv"
  39.  
  40. static void   query (void);
  41. static void   run   (gchar      *name,
  42.              gint        nparams,
  43.              GimpParam  *param,
  44.              gint       *nreturn_vals,
  45.              GimpParam **return_vals);
  46.  
  47. static GimpPDBStatusType scatter_hsv   (gint32  drawable_id);
  48. static void        scatter_hsv_scatter (guchar *r,
  49.                     guchar *g,
  50.                     guchar *b);
  51. static gint        randomize_value     (gint    now,
  52.                     gint    min,
  53.                     gint    max,
  54.                     gint    mod_p,
  55.                     gint    rand_max);
  56.  
  57. static gint    scatter_hsv_dialog         (void);
  58. static void    scatter_hsv_ok_callback    (GtkWidget     *widget,
  59.                         gpointer       data);
  60. static gint    preview_event_handler      (GtkWidget     *widget,
  61.                         GdkEvent      *event);
  62. static void    scatter_hsv_preview_update (void);
  63. static void     scatter_hsv_iscale_update  (GtkAdjustment *adjustment,
  64.                         gpointer       data);
  65.  
  66. #define PROGRESS_UPDATE_NUM 100
  67. #define PREVIEW_WIDTH       128
  68. #define PREVIEW_HEIGHT      128
  69. #define SCALE_WIDTH         100
  70.  
  71. static gint preview_width  = PREVIEW_WIDTH;
  72. static gint preview_height = PREVIEW_HEIGHT;
  73.  
  74. GimpPlugInInfo PLUG_IN_INFO =
  75. {
  76.   NULL,  /* init_proc  */
  77.   NULL,  /* quit_proc  */
  78.   query, /* query_proc */
  79.   run,   /* run_proc   */
  80. };
  81.  
  82. typedef struct
  83. {                /* gint, gdouble, and so on */
  84.   gint    holdness;
  85.   gint    hue_distance;
  86.   gint    saturation_distance;
  87.   gint    value_distance;
  88. } ValueType;
  89.  
  90. static ValueType VALS = 
  91. {
  92.   2,
  93.   3,
  94.   10,
  95.   10
  96. };
  97.  
  98. typedef struct 
  99. {
  100.   gint run;
  101. } Interface;
  102.  
  103. static Interface INTERFACE =
  104. {
  105.   FALSE
  106. };
  107.  
  108. static gint      drawable_id;
  109.  
  110. static GtkWidget *preview;
  111. static gint      preview_start_x = 0;
  112. static gint      preview_start_y = 0;
  113. static guchar     *preview_buffer = NULL;
  114. static gint      preview_offset_x = 0;
  115. static gint      preview_offset_y = 0;
  116. static gint      preview_dragging = FALSE;
  117. static gint      preview_drag_start_x = 0;
  118. static gint      preview_drag_start_y = 0;
  119.  
  120. MAIN ()
  121.  
  122. static void
  123. query (void)
  124. {
  125.   static GimpParamDef args [] =
  126.   {
  127.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
  128.     { GIMP_PDB_IMAGE, "image", "Input image (not used)"},
  129.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable"},
  130.     { GIMP_PDB_INT32, "holdness", "convolution strength"},
  131.     { GIMP_PDB_INT32, "hue_distance", "distribution distance on hue axis [0,255]"},
  132.     { GIMP_PDB_INT32, "saturation_distance", "distribution distance on saturation axis [0,255]"},
  133.     { GIMP_PDB_INT32, "value_distance", "distribution distance on value axis [0,255]"}
  134.   };
  135.   static gint nargs = sizeof (args) / sizeof (args[0]);
  136.  
  137.   gimp_install_procedure (PLUG_IN_NAME,
  138.               "Scattering pixel values in HSV space",
  139.               "Scattering pixel values in HSV space",
  140.               "Shuji Narazaki (narazaki@InetQ.or.jp)",
  141.               "Shuji Narazaki",
  142.               "1997",
  143.               N_("<Image>/Filters/Noise/Scatter HSV..."),
  144.               "RGB*",
  145.               GIMP_PLUGIN,
  146.               nargs, 0,
  147.               args, NULL);
  148. }
  149.  
  150. static void
  151. run (gchar   *name,
  152.      gint     nparams,
  153.      GimpParam  *param,
  154.      gint    *nreturn_vals,
  155.      GimpParam **return_vals)
  156. {
  157.   static GimpParam    values[1];
  158.   GimpPDBStatusType   status = GIMP_PDB_EXECUTION_ERROR;
  159.   GimpRunModeType  run_mode;
  160.   
  161.   run_mode = param[0].data.d_int32;
  162.   drawable_id = param[2].data.d_int32;
  163.  
  164.   *nreturn_vals = 1;
  165.   *return_vals = values;
  166.   
  167.   values[0].type = GIMP_PDB_STATUS;
  168.   values[0].data.d_status = status;
  169.  
  170.   switch (run_mode)
  171.     {
  172.     case GIMP_RUN_INTERACTIVE:
  173.       INIT_I18N_UI();
  174.       gimp_get_data (PLUG_IN_NAME, &VALS);
  175.       if (!gimp_drawable_is_rgb (drawable_id))
  176.     {
  177.       g_message ("Scatter HSV: RGB drawable is not selected.");
  178.       return;
  179.     }
  180.       if (! scatter_hsv_dialog ())
  181.     return;
  182.       break;
  183.     case GIMP_RUN_NONINTERACTIVE:
  184.       INIT_I18N();
  185.       VALS.holdness = param[3].data.d_int32;
  186.       VALS.hue_distance = param[4].data.d_int32;
  187.       VALS.saturation_distance = param[5].data.d_int32;
  188.       VALS.value_distance = param[6].data.d_int32;
  189.       break;
  190.     case GIMP_RUN_WITH_LAST_VALS:
  191.       INIT_I18N();
  192.       gimp_get_data (PLUG_IN_NAME, &VALS);
  193.       break;
  194.     }
  195.   
  196.   status = scatter_hsv (drawable_id);
  197.  
  198.   if (run_mode != GIMP_RUN_NONINTERACTIVE)
  199.     gimp_displays_flush();
  200.   if (run_mode == GIMP_RUN_INTERACTIVE && status == GIMP_PDB_SUCCESS )
  201.     gimp_set_data (PLUG_IN_NAME, &VALS, sizeof (ValueType));
  202.  
  203.   values[0].type = GIMP_PDB_STATUS;
  204.   values[0].data.d_status = status;
  205. }
  206.  
  207. static GimpPDBStatusType
  208. scatter_hsv (gint32 drawable_id)
  209. {
  210.   GimpDrawable *drawable;
  211.   GimpPixelRgn  src_rgn, dest_rgn;
  212.   guchar    *src, *dest;
  213.   gpointer   pr;
  214.   gint       x, y, x1, x2, y1, y2;
  215.   gint       gap, total, processed = 0;
  216.   
  217.   drawable = gimp_drawable_get (drawable_id);
  218.   gap = (gimp_drawable_has_alpha (drawable_id)) ? 1 : 0;
  219.   gimp_drawable_mask_bounds (drawable_id, &x1, &y1, &x2, &y2);
  220.   total = (x2 - x1) * (y2 - y1);
  221.   if (total < 1)
  222.     return GIMP_PDB_EXECUTION_ERROR;
  223.  
  224.   gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
  225.   gimp_pixel_rgn_init (&src_rgn, drawable,
  226.                x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE);
  227.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  228.                x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE);
  229.  
  230.   gimp_progress_init (_("Scatter HSV: Scattering..."));
  231. #ifdef G_OS_WIN32
  232.   SRAND_FUNC (time (NULL));
  233. #else
  234.   srand (time (NULL));
  235. #endif
  236.   pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
  237.   
  238.   for (; pr != NULL; pr = gimp_pixel_rgns_process (pr))
  239.     {
  240.       int offset;
  241.  
  242.       for (y = 0; y < src_rgn.h; y++)
  243.     {
  244.       src = src_rgn.data + y * src_rgn.rowstride;
  245.       dest = dest_rgn.data + y * dest_rgn.rowstride;
  246.       offset = 0;
  247.  
  248.       for (x = 0; x < src_rgn.w; x++)
  249.         {
  250.           guchar    h, s, v;
  251.  
  252.           h = *(src + offset);
  253.           s = *(src + offset + 1);
  254.           v = *(src + offset + 2);
  255.           
  256.           scatter_hsv_scatter (&h, &s, &v);
  257.  
  258.           *(dest + offset    ) = (guchar) h;
  259.           *(dest + offset + 1) = (guchar) s;
  260.           *(dest + offset + 2) = (guchar) v;
  261.  
  262.           offset += 3;
  263.           if (gap)
  264.         {
  265.           *(dest + offset) = *(src + offset);
  266.           offset++;
  267.         }
  268.           /* the function */
  269.           if ((++processed % (total / PROGRESS_UPDATE_NUM + 1)) == 0)
  270.         gimp_progress_update ((double) processed /(double) total); 
  271.         }
  272.     }
  273.   }
  274.  
  275.   gimp_progress_update (1.0);
  276.   gimp_drawable_flush (drawable);
  277.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  278.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  279.   gimp_drawable_detach (drawable);
  280.  
  281.   return GIMP_PDB_SUCCESS;
  282. }
  283.  
  284. static gint
  285. randomize_value (gint now,
  286.          gint min,
  287.          gint max,
  288.          gint mod_p,
  289.          gint rand_max)
  290. {
  291.   gint    flag, new, steps, index;
  292.   gdouble rand_val;
  293.   
  294.   steps = max - min + 1;
  295. #ifdef G_OS_WIN32
  296.   rand_val = ((double) RAND_FUNC () / (double) G_MAXRAND);
  297. #else
  298.   rand_val = ((double) rand () / (double) G_MAXRAND);
  299. #endif
  300.   for (index = 1; index < VALS.holdness; index++)
  301.     {
  302. #ifdef G_OS_WIN32
  303.       double tmp = ((double) RAND_FUNC () / (double) G_MAXRAND);
  304. #else
  305.       double tmp = ((double) rand () / (double) G_MAXRAND);
  306. #endif
  307.       if (tmp < rand_val)
  308.     rand_val = tmp;
  309.     }
  310.   
  311. #ifdef G_OS_WIN32
  312.   flag = ((G_MAXRAND / 2) < RAND_FUNC ()) ? 1 : -1;
  313. #else
  314.   flag = ((G_MAXRAND / 2) < rand()) ? 1 : -1;
  315. #endif
  316.   new = now + flag * ((int) (rand_max * rand_val) % steps);
  317.   
  318.   if (new < min)
  319.     {
  320.       if (mod_p == 1)
  321.     new += steps;
  322.       else
  323.     new = min;
  324.     }
  325.   if (max < new)
  326.     {
  327.       if (mod_p == 1)
  328.     new -= steps;
  329.       else
  330.     new = max;
  331.     }
  332.   return new;
  333. }
  334.  
  335. void scatter_hsv_scatter (guchar *r,
  336.               guchar *g,
  337.               guchar *b)
  338. {
  339.   gint h, s, v;
  340.   gint h1, s1, v1;
  341.   gint h2, s2, v2;
  342.   
  343.   h = *r; s = *g; v = *b;
  344.   
  345.   gimp_rgb_to_hsv (&h, &s, &v);
  346.  
  347.   if (0 < VALS.hue_distance)
  348.     h = randomize_value (h, 0, 255, 1, VALS.hue_distance);
  349.   if ((0 < VALS.saturation_distance))
  350.     s = randomize_value (s, 0, 255, 0, VALS.saturation_distance);
  351.   if ((0 < VALS.value_distance))
  352.     v = randomize_value (v, 0, 255, 0, VALS.value_distance);
  353.  
  354.   h1 = h; s1 = s; v1 = v;
  355.           
  356.   gimp_hsv_to_rgb (&h, &s, &v); /* don't believe ! */
  357.  
  358.   h2 = h; s2 = s; v2 = v;
  359.  
  360.   gimp_rgb_to_hsv (&h2, &s2, &v2); /* h2 should be h1. But... */
  361.   
  362.   if ((abs (h1 - h2) <= VALS.hue_distance)
  363.       && (abs (s1 - s2) <= VALS.saturation_distance)
  364.       && (abs (v1 - v2) <= VALS.value_distance))
  365.     {
  366.       *r = h;
  367.       *g = s;
  368.       *b = v;
  369.     }
  370. }
  371.  
  372. /* dialog stuff */
  373. static gint
  374. scatter_hsv_dialog (void)
  375. {
  376.   GtkWidget *dlg;
  377.   GtkWidget *vbox;
  378.   GtkWidget *frame;
  379.   GtkWidget *pframe;
  380.   GtkWidget *abox;
  381.   GtkWidget *table;
  382.   GtkObject *adj;
  383.  
  384.   gimp_ui_init (SHORT_NAME, TRUE);
  385.  
  386.   dlg = gimp_dialog_new (_("Scatter HSV"), SHORT_NAME,
  387.              gimp_standard_help_func, "filters/scatter_hsv.html",
  388.              GTK_WIN_POS_MOUSE,
  389.              FALSE, TRUE, FALSE,
  390.  
  391.              _("OK"), scatter_hsv_ok_callback,
  392.              NULL, NULL, NULL, TRUE, FALSE,
  393.              _("Cancel"), gtk_widget_destroy,
  394.              NULL, 1, NULL, FALSE, TRUE,
  395.  
  396.              NULL);
  397.  
  398.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  399.               GTK_SIGNAL_FUNC (gtk_main_quit),
  400.               NULL);
  401.  
  402.   vbox = gtk_vbox_new (FALSE, 6);
  403.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  404.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), vbox);
  405.  
  406.   frame = gtk_frame_new (_("Preview (1:4) - Right Click to Jump"));
  407.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  408.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  409.   gtk_widget_show (frame);
  410.  
  411.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  412.   gtk_container_add (GTK_CONTAINER (frame), abox);
  413.   gtk_widget_show (abox);
  414.  
  415.   pframe = gtk_frame_new (NULL);
  416.   gtk_frame_set_shadow_type (GTK_FRAME (pframe), GTK_SHADOW_IN);
  417.   gtk_container_set_border_width (GTK_CONTAINER (pframe), 4);
  418.   gtk_container_add (GTK_CONTAINER (abox), pframe);
  419.   gtk_widget_show (pframe);
  420.  
  421.   preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  422.   {
  423.     gint width  = gimp_drawable_width (drawable_id);
  424.     gint height = gimp_drawable_height (drawable_id);
  425.  
  426.     preview_width  = (PREVIEW_WIDTH  < width)  ? PREVIEW_WIDTH  : width;
  427.     preview_height = (PREVIEW_HEIGHT < height) ? PREVIEW_HEIGHT : height;
  428.   }
  429.   gtk_preview_size (GTK_PREVIEW (preview), preview_width * 2, preview_height);
  430.   scatter_hsv_preview_update ();
  431.   gtk_container_add (GTK_CONTAINER (pframe), preview);
  432.   gtk_widget_set_events (preview, 
  433.              GDK_BUTTON_PRESS_MASK |
  434.              GDK_BUTTON_RELEASE_MASK | 
  435.              GDK_BUTTON_MOTION_MASK |
  436.              GDK_POINTER_MOTION_HINT_MASK);
  437.   gtk_signal_connect (GTK_OBJECT (preview), "event",
  438.               (GtkSignalFunc) preview_event_handler,
  439.               NULL);
  440.   gtk_widget_show (preview);
  441.  
  442.   gtk_widget_show (frame);
  443.  
  444.   frame = gtk_frame_new (_("Parameter Settings"));
  445.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  446.   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  447.   gtk_widget_show (frame);
  448.  
  449.   table = gtk_table_new (4, 3, FALSE);
  450.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  451.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  452.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  453.   gtk_container_add (GTK_CONTAINER (frame), table);
  454.   gtk_widget_show (table);
  455.  
  456.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  457.                   _("Holdness:"), SCALE_WIDTH, 0,
  458.                   VALS.holdness, 1, 8, 1, 2, 0,
  459.                   TRUE, 0, 0,
  460.                   NULL, NULL);
  461.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  462.               GTK_SIGNAL_FUNC (scatter_hsv_iscale_update),
  463.               &VALS.holdness);
  464.  
  465.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  466.                   _("Hue:"), SCALE_WIDTH, 0,
  467.                   VALS.hue_distance, 0, 255, 1, 8, 0,
  468.                   TRUE, 0, 0,
  469.                   NULL, NULL);
  470.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  471.               GTK_SIGNAL_FUNC (scatter_hsv_iscale_update),
  472.               &VALS.hue_distance);
  473.  
  474.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
  475.                   _("Saturation:"), SCALE_WIDTH, 0,
  476.                   VALS.saturation_distance, 0, 255, 1, 8, 0,
  477.                   TRUE, 0, 0,
  478.                   NULL, NULL);
  479.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  480.               GTK_SIGNAL_FUNC (scatter_hsv_iscale_update),
  481.               &VALS.saturation_distance);
  482.  
  483.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
  484.                   _("Value:"), SCALE_WIDTH, 0,
  485.                   VALS.value_distance, 0, 255, 1, 8, 0,
  486.                   TRUE, 0, 0,
  487.                   NULL, NULL);
  488.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  489.               GTK_SIGNAL_FUNC (scatter_hsv_iscale_update),
  490.               &VALS.value_distance);
  491.  
  492.   gtk_widget_show (table);
  493.   gtk_widget_show (frame);
  494.  
  495.   gtk_widget_show (vbox);
  496.   gtk_widget_show (dlg);
  497.   
  498.   gtk_main ();
  499.   gdk_flush ();
  500.  
  501.   return INTERFACE.run;
  502. }
  503.  
  504. static gint
  505. preview_event_handler (GtkWidget *widget,
  506.                GdkEvent  *event)
  507. {
  508.   gint            x, y;
  509.   gint            dx, dy;
  510.   GdkEventButton *bevent;
  511.     
  512.   gtk_widget_get_pointer (widget, &x, &y);
  513.  
  514.   bevent = (GdkEventButton *) event;
  515.  
  516.   switch (event->type) 
  517.     {
  518.     case GDK_BUTTON_PRESS:
  519.       if (x < preview_width)
  520.     {
  521.       if (bevent->button == 3)
  522.         {
  523.           preview_offset_x = - x;
  524.           preview_offset_y = - y;
  525.           scatter_hsv_preview_update ();
  526.         }
  527.       else
  528.         {
  529.           preview_dragging = TRUE;
  530.           preview_drag_start_x = x;
  531.           preview_drag_start_y = y;
  532.           gtk_grab_add (widget);
  533.         }
  534.     }
  535.       break;
  536.     case GDK_BUTTON_RELEASE:
  537.       if (preview_dragging)
  538.     {
  539.       gtk_grab_remove (widget);
  540.       preview_dragging = FALSE;
  541.       scatter_hsv_preview_update ();
  542.     }
  543.       break;
  544.     case GDK_MOTION_NOTIFY:
  545.       if (preview_dragging)
  546.     {
  547.       dx = x - preview_drag_start_x;
  548.       dy = y - preview_drag_start_y;
  549.  
  550.       preview_drag_start_x = x;
  551.       preview_drag_start_y = y;
  552.  
  553.       if ((dx == 0) && (dy == 0))
  554.         break;
  555.  
  556.       preview_offset_x = MAX (preview_offset_x - dx, 0);
  557.       preview_offset_y = MAX (preview_offset_y - dy, 0);
  558.       scatter_hsv_preview_update ();
  559.     }
  560.       break; 
  561.     default:
  562.       break;
  563.     }
  564.   return FALSE;
  565. }
  566.  
  567. static void
  568. scatter_hsv_preview_update (void)
  569. {
  570.   GimpDrawable    *drawable;
  571.   GimpPixelRgn    src_rgn;
  572.   gint    scale;
  573.   gint    x, y, dx, dy;
  574.   gint    bound_start_x, bound_start_y, bound_end_x, bound_end_y;
  575.   gint    src_has_alpha = FALSE;
  576.   gint    src_is_gray = FALSE;
  577.   gint    src_bpp, src_bpl;
  578.   guchar    data[3];
  579.   gdouble    shift_rate;
  580.     
  581.   drawable = gimp_drawable_get (drawable_id);
  582.   gimp_drawable_mask_bounds (drawable_id,
  583.                  &bound_start_x, &bound_start_y,
  584.                  &bound_end_x, &bound_end_y);
  585.   src_has_alpha  = gimp_drawable_has_alpha (drawable_id);
  586.   src_is_gray =  gimp_drawable_is_gray (drawable_id);
  587.   src_bpp = (src_is_gray ? 1 : 3) + (src_has_alpha ? 1 : 0);
  588.   src_bpl = preview_width * src_bpp;
  589.  
  590.   if (! preview_buffer)
  591.     preview_buffer
  592.       = (guchar *) g_malloc (src_bpl * preview_height * sizeof (guchar));
  593.  
  594.   if (preview_offset_x < 0)
  595.     preview_offset_x = (bound_end_x - bound_start_x) * (- preview_offset_x) /  preview_width;
  596.   if (preview_offset_y < 0)
  597.     preview_offset_y = (bound_end_y - bound_start_y) * (- preview_offset_y) /  preview_height;
  598.   preview_start_x = CLAMP (bound_start_x + preview_offset_x,
  599.                bound_start_x, MAX (bound_end_x - preview_width, 0));
  600.   preview_start_y = CLAMP (bound_start_y + preview_offset_y,
  601.                bound_start_y, MAX (bound_end_y - preview_height, 0));
  602.   if (preview_start_x == bound_start_x)
  603.     preview_offset_x = 0;
  604.   if (preview_start_y == bound_start_y)
  605.     preview_offset_y =0;
  606.  
  607.   gimp_pixel_rgn_init (&src_rgn, drawable, preview_start_x, preview_start_y,
  608.                preview_width, preview_height,
  609.                FALSE, FALSE);
  610.  
  611.   /* Since it's small, get whole data before processing. */
  612.   gimp_pixel_rgn_get_rect (&src_rgn, preview_buffer,
  613.                preview_start_x, preview_start_y,
  614.                preview_width, preview_height);
  615.  
  616.   scale = 4;
  617.   shift_rate = (gdouble) (scale - 1) / ( 2 * scale);
  618.   for (y = 0; y < preview_height/4; y++)
  619.     {
  620.       for (x = 0; x < preview_width/4; x++)
  621.     {
  622.       gint pos;
  623.       gint    i;
  624.  
  625.       pos = (gint)(y + preview_height * shift_rate) * src_bpl
  626.             + (gint)(x + preview_width * shift_rate) * src_bpp;
  627.  
  628.       for (i = 0; i < src_bpp; i++)
  629.         data[i] = preview_buffer[pos + i];
  630.  
  631.       scatter_hsv_scatter (data+0, data+1, data+2);
  632.       for (dy = 0; dy < scale; dy++)
  633.         for (dx = 0; dx < scale; dx++)
  634.           gtk_preview_draw_row (GTK_PREVIEW (preview), data,
  635.                     preview_width + x * scale + dx,
  636.                     y * scale + dy, 1);
  637.     }
  638.     }
  639.   for (y = 0; y < preview_height; y ++)
  640.     for (x = 0; x < preview_width; x++)
  641.       {
  642.     gint    i;
  643.  
  644.     for (i = 0; i < src_bpp; i++)
  645.       data[i] = preview_buffer[y * src_bpl + x * src_bpp + i];
  646.  
  647.     scatter_hsv_scatter (data+0, data+1, data+2);
  648.     gtk_preview_draw_row (GTK_PREVIEW (preview), data, x, y, 1);
  649.       }
  650.   gtk_widget_draw (preview, NULL);
  651.   gdk_flush ();
  652. }
  653.  
  654. static void
  655. scatter_hsv_ok_callback (GtkWidget *widget,
  656.              gpointer   data)
  657. {
  658.   INTERFACE.run = TRUE;
  659.  
  660.   gtk_widget_destroy (GTK_WIDGET (data));
  661. }
  662.  
  663. static void
  664. scatter_hsv_iscale_update (GtkAdjustment *adjustment,
  665.                gpointer       data)
  666. {
  667.   gimp_int_adjustment_update (adjustment, data);
  668.  
  669.   scatter_hsv_preview_update ();
  670. }
  671.